package tcplib

import (
	"cow_golang/shared_mod/tcplib/iface"
	"errors"
	"fmt"
	"io"
	"net"
)

type Session struct {
	// 连接唯一id
	ID uint32
	// 当前socket tcp fd
	Conn *net.TCPConn
	// 当前连接的关闭状态
	closed bool
	// session从server实例中引用过来的协议对应处理map
	MsgHandles iface.IMessageHandle
	//通知关闭连接的channel
	ExitBuffChan chan bool
	// 消息发送队列（无缓冲）
	sendMsgChan chan []byte
	// 消息发送队列（有缓冲）
	sendMsgBuffChan chan []byte
	// 服务引用
	Server iface.IServer
}

func (c *Session) GetID() uint32 {
	return c.ID
}

func NewSession(conn *net.TCPConn, id uint32,
	msgHandles iface.IMessageHandle, server iface.IServer) *Session {
	c := &Session{
		ID:              id,
		Conn:            conn,
		closed:          false,
		MsgHandles:      msgHandles,
		ExitBuffChan:    make(chan bool, 1),
		sendMsgChan:     make(chan []byte),
		sendMsgBuffChan: make(chan []byte, MaxMsgChanLen),
		Server:          server,
	}
	// session 加入到管理模块
	c.Server.GetSessionMgr().Add(c)
	return c
}

func (c *Session) Start() {
	//循环读取网络消息
	go c.LoopRead()
	//循环发送队列消息
	go c.LoopWrite()

	//创建session，有新连接, 执行钩子调用
	c.Server.OnCreateConnHook(c)
	//阻塞
	for {
		select {
		case <-c.ExitBuffChan:
			// 得到消息退出
			return
		}
	}
}

func (c *Session) Stop() {
	if c.closed == true {
		return
	}
	c.closed = true

	// 关闭业务处理
	c.Server.OnCloseConnHook(c)
	// 关闭socket
	c.Conn.Close()
	// 通知退出
	c.ExitBuffChan <- true
	// 从session管理器中删除session
	c.Server.GetSessionMgr().Remove(c)
	// 关闭管道
	close(c.ExitBuffChan)
	close(c.sendMsgBuffChan)
}

// 获取socket fd
func (c *Session) GetTcpConn() *net.TCPConn {
	return c.Conn
}

// 获取session id
func (c *Session) GetId() uint32 {
	return c.ID
}

// 获取对端地址
func (c *Session) RemoteAddr() net.Addr {
	return c.Conn.RemoteAddr()
}

// 循环读取
func (c *Session) LoopRead() {
	fmt.Println("Session LoopRead is running...")
	defer fmt.Println(c.RemoteAddr().String, "conn reader exit!")
	defer c.Stop()

	for {
		// 1 message unpack
		// 2 判断是否打开包统计功能
		// 3 判断是否异或加解密
		// 4 decode protobuf
		// 5并发模型分配器 -> 协议分析 -> 分配不同的并发处理

		//创建拆包对象
		dp := NewMessagePack()
		//读取包头
		head := make([]byte, dp.GetHeadLen())
		if _, err := io.ReadFull(c.Conn, head); err != nil {
			fmt.Println("Session Read head Error:", err)
			c.ExitBuffChan <- true
			continue
		}
		// 解析包头，获得消息id和数据长度
		msg, err := dp.UnPack(head)
		if err != nil {
			fmt.Println("Session unpack Error:", err)
			c.ExitBuffChan <- true
			continue
		}
		// 读取数据
		var data []byte
		if msg.GetDataLen() > 0 {
			//创建读取数据的buff
			data := make([]byte, msg.GetDataLen())
			if _, err := io.ReadFull(c.Conn, data); err != nil {
				if err != nil {
					fmt.Println("Session Read data Error:", err)
					c.ExitBuffChan <- true
					continue
				}
			}
		}
		msg.SetData(data)
		// 组织request数据
		request := Request{
			session: c,
			msg:     msg,
		}
		go c.MsgHandles.CallMessageHandle(&request)
	}
}

// 循环写入
func (c *Session) LoopWrite() {
	fmt.Println("Session LoopWrite is running...")
	defer fmt.Println(c.RemoteAddr().String, "conn write exit!")

	for {
		select {
		case data := <-c.sendMsgChan:
			// 如果有数据，就发送
			if _, err := c.Conn.Write(data); err != nil {
				fmt.Println("Session Write Error:", err)
				return
			}
		case data, ok := <-c.sendMsgBuffChan:
			if ok {
				if _, err := c.Conn.Write(data); err != nil {
					fmt.Println("Session Write buff Error:", err)
					return
				}
			} else {
				fmt.Println("sendMsgBuffChan is closed")
				break
			}
		case <-c.ExitBuffChan:
			return
		}
	}
}

// 发送消息
func (c *Session) Send(msgId uint32, data []byte) error {
	if MaxMsgChanLen > 0 {
		return c.SendBuffMsg(msgId, data)
	}
	return c.SendMsg(msgId, data)
}

// 发送无缓冲
func (c *Session) SendMsg(msgId uint32, data []byte) error {
	if c.closed {
		return errors.New("Session closed not send msg")
	}
	// 封包
	db := NewMessagePack()
	msg, err := db.Pack(NewMessage(msgId, data))
	if err != nil {
		fmt.Println("Session Pack Error msg id:", msgId)
		return errors.New("Session Pack Error")
	}
	// 封包丢进发送队列
	c.sendMsgChan <- msg

	return nil
}

// 发送有缓冲的
func (c *Session) SendBuffMsg(msgId uint32, data []byte) error {
	if c.closed {
		return errors.New("Session closed not send msg")
	}
	// 封包
	db := NewMessagePack()
	msg, err := db.Pack(NewMessage(msgId, data))
	if err != nil {
		fmt.Println("Session Pack Error msg id:", msgId)
		return errors.New("Session Pack Error")
	}
	// 封包丢进发送队列
	c.sendMsgBuffChan <- msg

	return nil
}
